#
# Copyright 2010 Steve Leach
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
#

import logging
from datetime import datetime, date, timedelta

from google.appengine.ext import db

from handlers import EveOpsHandler, BaseHandler
from evedata import EveData, get_eve_data

class SolarSystem(db.Model):
	system_name = db.StringProperty(required=True, indexed=True)
	constellation_key = db.StringProperty()
	region_key = db.StringProperty()
	static_data = db.StringProperty()
	sec_status = db.FloatProperty()

class WHSystem(db.Model):
	"""Basic, unchanging WH system details"""
	system_name = db.StringProperty(required=True, indexed=True)
	constellation_key = db.StringProperty()
	region_key = db.StringProperty()
	static_data = db.StringProperty()

	def static_details(self):
		if self.static_data:
			parts = self.static_data.split()
			if len(parts) == 3:
				return parts[0] + ", size " + parts[1] + " to " + parts[2]
			elif len(parts) == 4:
				return parts[0] + ", size " + parts[1] + " to " + parts[2] + " " + parts[3]
			else:
				return parts
		else:
			return ""

class Sig(db.Model):
	"""
	Represents a scanning signature.
	"""
	system = db.ReferenceProperty(required=False, indexed=True)
	system_name = db.StringProperty(required=True, indexed=True, default='Unknown')
	code = db.StringProperty(required=True)
	sigtype = db.StringProperty()
	signame = db.StringProperty()
	sigsize = db.FloatProperty()
	entered_by = db.StringProperty()
	claimed_by = db.StringProperty()
	destination = db.StringProperty(default='')
	timestamp = db.DateTimeProperty(required=True, auto_now=True)
	entered_by = db.StringProperty()

class SysNotes(db.Model):
	"""
	Notes relating to a solar system.
	"""
	system_name = db.StringProperty(required=True, indexed=True)
	nickname = db.StringProperty(indexed=True)
	notes = db.TextProperty()
	is_prime = db.BooleanProperty(required=True, default=False)
	edited_by = db.StringProperty()
	edited_on = db.DateTimeProperty(auto_now=True)

def add_scanning_mappings(mappings):
	mappings.append(('/scanning', MapHandler2))
	mappings.append(('/viewsystem', ViewSystemHandler))
	mappings.append(('/viewsystem2', ViewSystemHandler))
	mappings.append(('/staticdata', ViewSystemHandler))
	mappings.append(('/scanningcalc', ScanCalcHandler))
	mappings.append(('/sigs', SigHandler))
	mappings.append(('/sysnotes', NotesHandler))
	mappings.append(('/sigcleanup', SigCleanupHandler))

class ScanningCalc(db.Model):
	char_name = db.StringProperty(required=True)
	covops_skill = db.IntegerProperty(default=0)
	ar_skill = db.IntegerProperty(default=0)
	scanner_bonus = db.IntegerProperty(default=0)
	probe_bonus = db.IntegerProperty(default=0)
	grav_rigs = db.IntegerProperty(default=0)
	implant_bonus = db.FloatProperty(default=0.0)
	total_bonus = db.FloatProperty(default=1.0)
	max_strength = db.FloatProperty(default=0.0)
	target_size = db.FloatProperty(default=6.67)
	scan_range = db.FloatProperty(default=32.0)
	distance = db.FloatProperty(default=0.0)
	strength = db.FloatProperty(default=0.0)

	def calc_max(self):
		base_strength = self.target_size * 5.0 / 12800.0

		covops_bonus = 1.0 + 0.1 * self.covops_skill
		ar_bonus = 1.0 + 0.1 * self.ar_skill

		launcher_bonus = 1.0 + 0.01 * self.scanner_bonus
		probes_bonus = 1.0 + 0.01 * self.probe_bonus

		if self.grav_rigs == 0:
			rig_bonus = 1.0
		elif self.grav_rigs == 1:
			rig_bonus = 1.1
		elif self.grav_rigs == 2:
			rig_bonus = 1.21

		implants = 1.0 + self.implant_bonus / 100.0

		self.total_bonus = covops_bonus * ar_bonus * launcher_bonus * probes_bonus * rig_bonus * implants
		
		bonus_strength = base_strength * self.total_bonus 

		# brute force, we should probably try an intelligent solution
		if self.scan_range == 256:
			self.max_strength = bonus_strength * 0.5
		elif self.scan_range == 128:
			self.max_strength = bonus_strength * 1.0
		elif self.scan_range == 64:
			self.max_strength = bonus_strength * 2.0
		elif self.scan_range == 32:
			self.max_strength = bonus_strength * 4.0
		elif self.scan_range == 16:
			self.max_strength = bonus_strength * 8.0
		elif self.scan_range == 8:
			self.max_strength = bonus_strength * 16.0
		elif self.scan_range == 4:
			self.max_strength = bonus_strength * 32.0
		elif self.scan_range == 2:
			self.max_strength = bonus_strength * 64.0
		elif self.scan_range == 1:
			self.max_strength = bonus_strength * 128.0
		elif self.scan_range == 0.5:
			self.max_strength = bonus_strength * 256.0
		elif self.scan_range == 0.25:
			self.max_strength = bonus_strength * 512.0
		else:
			self.max_strength = -1
		self.max_strength *= 100

		dist_ratio = self.distance / self.scan_range
		dist_mod = pow(2.7182, -(dist_ratio * dist_ratio))

		self.strength = self.max_strength * dist_mod

class ScanCalcHandler(EveOpsHandler):
	def get(self): 
		eve_data = get_eve_data(self.request.headers)

		if not self.authorised(eve_data):
			return	

		query = ScanningCalc.gql('WHERE char_name = :1', eve_data.char_name)
		if query.count() == 0:
			calcs =	ScanningCalc(char_name=eve_data.char_name)
			calcs.put()
		else:
			calcs = query.get()

		values = {'calcs': calcs, 'eve_data': eve_data, 'headers': self.request.headers}

		self.display_page( 'scanningcalc.html', values )

	def post(self):
		eve_data = get_eve_data(self.request.headers)

		if not self.authorised(eve_data):
			return	

		calcs = ScanningCalc.gql('WHERE char_name = :1', eve_data.char_name).get()
		calcs.covops_skill = int(self.request.get('covops_skill'))
		calcs.ar_skill = int(self.request.get('ar_skill'))
		calcs.scanner_bonus = int(self.request.get('scanner_bonus'))
		calcs.probe_bonus = int(self.request.get('probe_bonus'))
		calcs.grav_rigs = int(self.request.get('grav_rigs'))
		calcs.implant_bonus = float(self.request.get('implant_bonus'))
		calcs.target_size = float(self.request.get('target_size'))
		calcs.scan_range = float(self.request.get('scan_range'))
		calcs.distance = float(self.request.get('distance'))
		calcs.calc_max()
		calcs.put()

		self.redirect('/scanningcalc')

class SigHandler(BaseHandler):

	def do_get(self):  
		sigs = get_sigs(self.request.get('sysname'))			

		self.pagevalues['sysname'] = self.request.get('sysname')
		self.pagevalues['sigs'] = sigs

	  	self.show_page('sigs.html')

	def sig_changed(self, sigcode):
		type_old = self.request.get('orig_type_' + sigcode)
		type_new = self.request.get('sigtype_' + sigcode)
		dest_old = self.request.get('orig_dest_' + sigcode)
		dest_new = self.request.get('dest_' + sigcode)

		return (type_new != type_old) or (dest_new != dest_old)

	def do_post(self):
		sysname =  self.request.get('sysname')

		if not sysname:
			raise "Sysname must not be empty"

		sigs = Sig.gql('WHERE system_name = :1', sysname)			

		for sig in sigs:
			if self.request.get('delete_' + sig.code):
				sig.delete()
			elif self.sig_changed(sig.code):
				sig.system_name = sysname
				sig.sigtype = self.request.get('sigtype_' + sig.code)
				dest_name = self.request.get('dest_' + sig.code)
				dest_wh = find_wh(dest_name)
				if dest_wh:
					dest_name = dest_wh.system_name
				sig.destination = dest_name
				sig.entered_by = self.current_pilot_name()
				sig.put()

		if self.request.get('new_sigs'):
			new_sigs = self.request.get('new_sigs').split(' ')
			for sig in new_sigs:
				current = Sig.gql('WHERE system_name = :1 AND code = :2', sysname, sig)
				if current.count() == 0:
					newsig = Sig(system_name=sysname, code=sig.upper())
					newsig.sigtype = 'Unknown'
					newsig.destination = ''
					newsig.entered_by = self.current_pilot_name()
					newsig.put()

		self.redirect('/sigs?sysname='+sysname)

def get_sigs(sys_name):
	sigs = Sig.gql('WHERE system_name = :1 ORDER BY code', sys_name)
	return sigs

def get_category(region_key):
	if region_key == '11000030':
		return 'C6'
	elif region_key >= '11000024' and region_key <= '11000029':
		return 'C5'
	elif region_key >= '11000016' and region_key <= '11000023':
		return 'C4'
	elif region_key >= '11000009' and region_key <= '11000015':
		return 'C3'
	elif region_key >= '11000004' and region_key <= '11000008':
		return 'C2'
	elif region_key >= '11000001' and region_key <= '11000003':
		return 'C1'
	else:
		return '??'

def find_wh(system_name):
	wh = WHSystem.gql('WHERE system_name = :1', system_name.upper()).get()
	return wh

class ViewSystemHandler(BaseHandler):
	def do_get(self):  
		sys_name = self.request.get('name')

		wsys = find_wh(sys_name)
		
		if wsys:
			sys_name = wsys.system_name

		notes = SysNotes.gql('WHERE system_name = :1', sys_name).get()

		self.pagevalues['sysname'] = sys_name
		self.pagevalues['wsys'] = wsys
		self.pagevalues['sigs'] = get_sigs(sys_name)

		if notes:
			self.pagevalues['notes'] = notes.notes
			self.pagevalues['nickname'] = notes.nickname

		if wsys:
			self.pagevalues['category'] = get_category(wsys.region_key)
		else:
			self.pagevalues['category'] = 'kspace'

	  	self.show_page('viewsys2.html')

class MapNode2():
	pass

def createNode(name, notes):
	node = MapNode2()
	node.name = name
	if notes:
		node.nickname = notes.nickname
	wh_sys = WHSystem.gql('WHERE system_name = :1', name).get()
	if wh_sys:
		node.wh_class = get_category(wh_sys.region_key)
	return node

def empty_above(grid, row, col):
	return (row > 1) and (col < len(grid[row-1])-1) and (grid[row-1][col+1] == None)

def empty_left(grid, row, col):
	return (row >= 0) and (col >= 0) and (grid[row][col] == None)

def add_lines(node, grid, row, col):
	if col < 0:
		node.prefix0 = ''
		node.prefix1 = ''
	elif empty_left(grid, row, col):
		node.prefix0 = '&#124;'
		node.prefix1 = '&lsquo;&mdash;&mdash;&gt;'
		above_row = row
		while (above_row > 0):
			if empty_above(grid, above_row, col):
				dummy = MapNode2()
				dummy.prefix0 = '&#124;'
				dummy.prefix1 = '&#124;'
				grid[above_row-1][col+1] = dummy
			else:
				break
			above_row -= 1
	else:
		node.prefix0 = ''
		node.prefix1 = '&mdash;&mdash;&mdash;&gt;'
		

def add_node_2(sysname, grid, x_offset, notes, visited):

	visited.append(sysname)

	n = len(grid)-1
	m = len(grid[n])-1
	node = createNode(sysname, notes)
	add_lines(node, grid, n, m)
	grid[n].append(node)
	m = len(grid[n])
	first = True

	sigs = Sig.gql('WHERE system_name = :1 ORDER BY destination', sysname)
	for sig in sigs:
		if sig.destination and not sig.destination in visited:
			if not first:
				child_row = []
				for i in range(1,m+1):
					child_row.append(None)
				grid.append(child_row)
			add_node_2(sig.destination, grid, x_offset, None, visited)
			first = False

class MapHandler2(BaseHandler):
	def do_get(self):
		primes = SysNotes.gql('WHERE is_prime = True')

		grid = [[]]
		visited = []

		for prime in primes:
			grid.append([])
			add_node_2(prime.system_name, grid, 0, prime, visited)

		self.pagevalues['primes'] = primes
		self.pagevalues['grid'] = grid
	  	self.show_page('map2.html')
		
class NotesHandler(BaseHandler):
	def do_get(self):
		sys_name = self.request.get('sysname')
		notes = SysNotes.gql('WHERE system_name = :1', sys_name).get()

		self.pagevalues['sysname'] = sys_name
		self.pagevalues['notes'] = notes

	  	self.show_page('sysnotes.html')

	def do_post(self):
		sys_name = self.request.get('sysname')
		notes = SysNotes.gql('WHERE system_name = :1', sys_name).get()
		if not notes:
			notes = SysNotes(system_name=sys_name)
		notes.notes = self.request.get('notes')
		notes.nickname = self.request.get('nickname')
		notes.is_prime = self.request.get('prime') == 'prime'
		notes.put()

		self.redirect('/viewsystem2?name='+sys_name)

class SigCleanupHandler(BaseHandler):
	"""
	Clean-up all sigs that are more than 24 hours old.
	"""

	def do_get(self):
		self.cleanup()

	def do_post(self):
		self.cleanup()

	def cleanup(self):
		if self.request.get('submit') == 'Yes':
			cutoff = datetime.now() - timedelta(days=2)

			logging.info('%s cleaning up wormhole sigs from before %s' % (self.eve_data.char_name,str(cutoff)))
			count = 0

			for sig in Sig.all():
				if sig.timestamp < cutoff and sig.sigtype == 'Wormhole':
					count += 1
					sig.delete()

			logging.info('%d sigs deleted' % (count))

		self.redirect('/scanning')

